package com.springcachetest.Configure;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.lang.reflect.Method;

/**
 * @author 杨志远[of2551]
 *         company qianmi.com
 *         Date    2017-03-14
 */

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    //如果不注入这个RedisCacheManager，就会使用默认的RedisCacheManager，这样redis连不上，加有springCache注解的方法，就会直接返回异常，正常的业务无法执行。
    @Bean
    public RedisCacheManager redisCacheManager(
            @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {

        //使用默认的RedisCacheManager，cache处理中出现的异常，会导致异常抛出，而不是继续走cache后面的业务。
        //如，redis练级出现异常，使用默认的，会直接抛异常，而不走dao的去查数据库的业务。
        //此外的异常还有jackson序列化时出现的异常
//        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);

        //使用重写Cache的RedisFallbackCacheManager
        RedisCacheManager redisCacheManager = new RedisFallbackCacheManager(redisTemplate);

        //设置cacheName的TTL时间
//        redisCacheManager.setExpires(CacheNames.cacheNameExpireMap);

        //设置默认的TTL的时间
        redisCacheManager.setDefaultExpiration(600);

        //将userPrefix设置为true，并且设置cachePrefix后，cacheName~keys这个ZSet才不会产生
        //原因：猜测，cacheName~keys是springCache负责管理cache的一个键，如果每一个cache的key有前缀的化，效果是可以通过前缀来辨识cache，从而起到管理cache的效果
        redisCacheManager.setUsePrefix(true);
        redisCacheManager.setCachePrefix(cacheName -> "pm-".getBytes());
        return redisCacheManager;
    }


    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getSimpleName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    @Bean
    public KeyGenerator CachekeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getSimpleName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /**
     * redisCache默认的key生成器
     * @return
     */
    /*@Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getSimpleName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };

    }*/

    /**redis缓存--strat--*/
    /*@Bean
    public RedisCacheManager redisCacheManager(
            @SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
        //将userPrefix设置为true，并且设置cachePrefix后，cacheName^keys这个ZSet才不会产生
        //原因：猜测，cacheName^keys是springCache负责管理cache的一个键，如果每一个cache的key有前缀的化，效果是可以通过前缀来辨识cache，从而起到管理cache的效果
//        redisCacheManager.setUsePrefix(true);
//        redisCacheManager.setCachePrefix(cacheName -> "test-".getBytes());


        return redisCacheManager;
    }*/
    /**redis缓存--end--*/

    /**ehcache缓存--strat--*/
   /* @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
        System.out.println("CacheConfiguration.ehCacheManagerFactoryBean()");
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
        cacheManagerFactoryBean.setConfigLocation (new ClassPathResource("ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);
        return cacheManagerFactoryBean;
    }

    @Bean
    public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean bean){
        System.out.println("CacheConfiguration.ehCacheCacheManager()");
        return new EhCacheCacheManager(bean.getObject());
    }
*/
    /**ehcache缓存--end--*/


    @Bean
    public ZSetOperations<Object, Object> zSetOperations(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        return redisTemplate.opsForZSet();
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory factory) {
//         StringRedisTemplate RedisTemplate = new StringRedisTemplate(factory);
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
        redisTemplate.setConnectionFactory(factory);


        //序列化用的,这里是Jackson的
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //使用jackson
        ObjectMapper om = new ObjectMapper();
        //默认的配置是om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.PUBLIC_ONLY);
        //推荐的配置是om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.PUBLIC_ONLY);    //不会对private、protected的getXXX（）方法的XXX进行序列化
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);


        //
//        new JacksonJsonRedisSerializer（Object.class）;
//        new JacksonJsonRedisSerializer（Object.class）;


        //对于value，不能直接用StringRedisSerializer、JdkSerializationRedisSerializer，因为对象需要转换为字符串。
        redisTemplate.setKeySerializer(new GenericToStringSerializer<Object>(Object.class));
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.afterPropertiesSet();

        //JDK // TODO
        //JdkSerializationRedisSerializer JdkSerializationRedisSerializer;
        /*RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(factory);
        //使用jdk序列化对value进行序列化
        template.setValueSerializer(new JdkSerializationRedisSerializer());
        //使用string类型对key进行序列化
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();*/

//        JdkSerializationRedisSerializer = new JdkSerializationRedisSerializer(Object.class);
        //
        return redisTemplate;
    }


}
